/*
 * CastDRAndroid
 * Copyright (c) 2014 Christian Holm Christensen
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 *
 */
package org.cholm.media.castdr;

import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.media.MediaRouter;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.common.images.WebImage;
import com.google.sample.castcompanionlibrary.cast.BaseCastManager;
import com.google.sample.castcompanionlibrary.cast.VideoCastManager;
import com.google.sample.castcompanionlibrary.cast.callbacks.IVideoCastConsumer;
import com.google.sample.castcompanionlibrary.cast.callbacks.VideoCastConsumerImpl;

import org.cholm.media.castdr.adapters.DrawerAdapter;
import org.cholm.media.castdr.adapters.DrawerAdapter.Holder;
import org.cholm.media.castdr.backend.Category;
import org.cholm.media.castdr.backend.Extractor;
import org.cholm.media.castdr.backend.LiveChannel;
import org.cholm.media.castdr.backend.Program;
import org.cholm.media.castdr.backend.Search;
import org.cholm.media.castdr.fragments.SubCategoryFragment;
import org.cholm.media.castdr.fragments.TopFragment;
import org.cholm.media.castdr.loaders.CategoryLoader;

import java.util.List;

import static org.cholm.media.castdr.LogUtils.LOGd;
import static org.cholm.media.castdr.LogUtils.LOGw;

/**
 * - This activity starts out with a progress bar in the main view. - It then
 * make an instance of a loader to load the categories. - On completion of that
 * loading, we replace the progress bar with a category navigation fragment.
 * <p/>
 * The activity also defines a drawer, which however is empty for now
 * 
 * @author cholm
 */
@SuppressWarnings("deprecation")
public class Activity extends ActionBarActivity {

	/**
	 * Log TAG
	 */
	private static final String TAG = LogUtils.makeLogTag(Activity.class);
	private static final String TOP_FRAGMENT = "main";
	/**
	 * Top layout
	 */
	private DrawerLayout mDrawerLayout = null;
	/**
	 * Adapter for drawer
	 */
	private DrawerAdapter mDrawerAdapter = null;
	/**
	 * Toggle
	 */
	private DrawerToggle mDrawerToggle = null;
	/**
	 * List in drawer
	 */
	private ListView mDrawerList = null;

	// ===============================================================
	// Cast interface
	/**
	 * Cast maanger
	 */
	private VideoCastManager mCastMgr = null;
	/**
	 * Consumer
	 */
	private IVideoCastConsumer mCastConsumer = null;
	/**
	 * Mini controller
	 */
	private MiniController mMini = null;
	/**
	 * Entry in action bar
	 */
	private MenuItem mMediaRouteMenuItem = null;
	/**
	 * Currently selected media
	 */
	private MediaInfo mSelectedMedia = null;

	/**
     *
     */
	public Activity() {
	}

	public void onCreate(Bundle savedInstanceState) {
		LOGd(TAG, "Creating activity");
		super.onCreate(savedInstanceState);
		setContentView(org.cholm.media.castdr.R.layout.activity);

		ActionBar actionBar = getSupportActionBar();
		actionBar.setHomeButtonEnabled(true);
		actionBar.setDisplayHomeAsUpEnabled(true);

		mDrawerLayout = (DrawerLayout) findViewById(org.cholm.media.castdr.R.id.drawer_layout);
		mDrawerAdapter = new DrawerAdapter(this);
		mDrawerToggle = new DrawerToggle(this);
		mDrawerList = (ListView) findViewById(org.cholm.media.castdr.R.id.left_drawer);
		mDrawerList.setAdapter(mDrawerAdapter);
		mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
		mDrawerLayout.setDrawerListener(mDrawerToggle);

		// Do cast set-up
        BaseCastManager.checkGooglePlaySevices(this);

        mCastMgr = Application.getCastManager(this);

        // -- Adding MiniController
        LOGd(TAG, "Adding mini controller");
        mMini = (MiniController) findViewById(org.cholm.media.castdr.R.id.miniController1);
        mMini.setVisibility(View.GONE);
        mCastMgr.addMiniController(mMini);


        // Our local specialisation
        mCastConsumer = new VideoCastConsumerImpl() {
            public void onApplicationConnected(ApplicationMetadata appMetadata,
                                               String sessionId,
                                               boolean wasLaunched) {
                LOGd(TAG, "onApplicationLaunched() is reached");
                super.onApplicationConnected(appMetadata, sessionId,
                        wasLaunched);
            }

            @Override
            public void onApplicationDisconnected(int errorCode) {
                LOGd(TAG,
                        "onApplicationDisconnected() is reached with errorCode: "
                                + errorCode);
                super.onApplicationDisconnected(errorCode);
            }

            @Override
            public void onDisconnected() {
                LOGd(TAG, "onDisconnected() is reached");
                super.onDisconnected();
            }

            @Override
            public void onRemoteMediaPlayerMetadataUpdated() {
                // try {
                // mRemoteMediaInformation =
                // / mCastManager.getRemoteMediaInformation();
                // }
                // catch (Exception e) {
                // silent
                // }
                LOGd(TAG, "Meta data update");
                super.onRemoteMediaPlayerMetadataUpdated();
            }

            @Override
            public void onFailed(int resourceId, int statusCode) {
                LOGd(TAG, "Failed to load " + resourceId + "/" + statusCode);
            }

            @Override
            public void onConnectionSuspended(int cause) {
                LOGd(TAG, "onConnectionSuspended() was called with cause: "
                        + cause);
                Toast.makeText(Activity.this, org.cholm.media.castdr.R.string.connection_temp_lost,
                        Toast.LENGTH_LONG).show();

            }

            @Override
            public void onConnectivityRecovered() {
                Toast.makeText(Activity.this, org.cholm.media.castdr.R.string.connection_recovered,
                        Toast.LENGTH_LONG).show();
            }

            @Override
            public void onCastDeviceDetected(MediaRouter.RouteInfo inf) {
                LOGd(TAG, "Route is visible: " + inf);
                final MediaRouter.RouteInfo i = inf;
                new Handler().postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        if (mMediaRouteMenuItem.isVisible()) {
                            LOGd(TAG,  "Cast Icon is visible: " + i.getName());
                            // showFtu();
                        }
                    }
                }, 1000);
            }
        };


		TopFragment t = getTop();
		if (t != null) {
			LOGd(TAG, "Already have top fragment");
			hideInitial(false);
		} else {
			LOGd(TAG, "Initialize the loader");
			getSupportLoaderManager().initLoader(org.cholm.media.castdr.R.integer.category_loader,
			                                     null, new CategoryCallback());
		}

        // Reconnect to cast session
        mCastMgr.reconnectSessionIfPossible(this, false);
    }

    /**
     * Creating user menu
     *
     * @param menu Menu to add to
     *
     * @return true on success
     */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu items for use in the action bar
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(org.cholm.media.castdr.R.menu.main, menu);
		MenuItem searchItem = menu.findItem(org.cholm.media.castdr.R.id.action_search);
		SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
		searchView.setOnQueryTextListener(new SearchListener());
		MenuItemCompat.setOnActionExpandListener(searchItem,
		                                         new SearchExpandListener());

		// Do cast setup
        mMediaRouteMenuItem = mCastMgr.addMediaRouterButton(menu,
                org.cholm.media.castdr.R.id.media_route_menu_item);

		return super.onCreateOptionsMenu(menu);
	}

	@Override
	protected void onPostCreate(Bundle savedInstanceState) {
		super.onPostCreate(savedInstanceState);
		// Sync the toggle state after onRestoreInstanceState has occurred.
		if (mDrawerToggle != null) mDrawerToggle.syncState();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		mDrawerToggle.onConfigurationChanged(newConfig);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Pass the event to ActionBarDrawerToggle, if it returns
		// true, then it has handled the app icon touch event
		if (mDrawerToggle.onOptionsItemSelected(item)) { return true; }
		// Handle your other action bar items...

		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onPause() {
		LOGd(TAG, "Pausing application");
        mCastMgr.decrementUiCounter();
        mCastMgr.removeVideoCastConsumer(mCastConsumer);

        super.onPause();
	}

	@Override
	public void onResume() {
		LOGd(TAG, "Resuming application");

        mCastMgr = Application.getCastManager(this);
        if (null != mCastMgr) {
            mCastMgr.addVideoCastConsumer(mCastConsumer);
            mCastMgr.incrementUiCounter();
        }
        boolean alwaysCtrl = PreferenceManager.getDefaultSharedPreferences
                (this).getBoolean("always_controller", false);
        if (alwaysCtrl) mMini.setVisibility(View.VISIBLE);

		super.onResume();
	}

	@Override
	public void onDestroy() {
        LOGd(TAG, "Destroying activity");
		if (null != mCastMgr) {
            LOGd(TAG, " Removing mini controller listener");
            mMini.removeOnMiniControllerChangedListener(mCastMgr);
            mCastMgr.removeMiniController(mMini);
            mCastMgr.clearContext(this);
            mCastMgr = null;
        }

		super.onDestroy();
	}

	private void hideInitial(boolean alsoContent) {
		mDrawerLayout.findViewById(org.cholm.media.castdr.R.id.progressBar).setVisibility(View.GONE);
		mDrawerLayout.findViewById(org.cholm.media.castdr.R.id.progressText).setVisibility(View.GONE);
		if (!alsoContent) return;
		mDrawerLayout.findViewById(org.cholm.media.castdr.R.id.contentFrame).setVisibility(View.GONE);
	}

	/**
	 * Show the category fragment - the top-most navigation fragment
	 * 
	 * @param data
	 *            Categories from loader
	 */
	public void showCategories(List<Category> data) {
		LOGd(TAG, "Show categories");
		hideInitial(false);

		FragmentManager fragmentManager = this.getSupportFragmentManager();
		FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
		TopFragment frag = new TopFragment();

		fragmentTransaction.replace(org.cholm.media.castdr.R.id.contentFrame, frag, TOP_FRAGMENT);
		// fragmentTransaction.addToBackStack(null);
		fragmentTransaction.commitAllowingStateLoss();
	}

	/**
	 * Handle selects in drawer
	 * 
	 * @param position
	 *            Position
	 * @param view
	 *            View
	 */
	public void selectItem(int position, View view) {
		LOGd(TAG, "Selected item " + position);
		Holder h = (Holder) view.getTag();
		if (h != null) h.update(true);
		Category c = mDrawerAdapter.getCategory(position);

        if (c == mDrawerAdapter.getSettings()) {
            mDrawerLayout.closeDrawers();
            // Launch settings activity
            Intent i = new Intent(Activity.this, Settings.class);
            startActivity(i);
            return;
        }

        TopFragment t = getTop();
        if (t == null) {
            LOGw(TAG, "No top fragment");
            return;
        }
        ViewPager p = t.getPager();
        if (p == null) {
            LOGw(TAG, "No pager");
        }
        try {
            p.setCurrentItem(position, true);
        }
        catch (NullPointerException e) {
            LOGw(TAG , "Failed to scroll to page " + position, e);
        }
        mDrawerLayout.closeDrawers();
    }

	private TopFragment getTop() {
		FragmentManager fragmentManager = this.getSupportFragmentManager();
		Fragment f = fragmentManager.findFragmentByTag(TOP_FRAGMENT);
		if (f == null) {
			LOGd(TAG, "Didn't find the main fragment");
			return null;
		}
		if (!(f instanceof TopFragment)) {
			LOGd(TAG, TOP_FRAGMENT + " fragment not instance of TopFragment");
			return null;
		}
		return (TopFragment) f;
	}

	private SubCategoryFragment getSub(String cat, String sub) {
		FragmentManager fragmentManager = this.getSupportFragmentManager();
		Fragment f = fragmentManager.findFragmentByTag(cat + ":" + sub);
		if (f == null) {
			LOGd(TAG, "Didn't find the fragment " + cat + ":" + sub);
			return null;
		}
		if (!(f instanceof SubCategoryFragment)) {
			return null;
		}
		return (SubCategoryFragment) f;
	}

	/**
	 * Handle search request
	 * 
	 * @param query
	 *            Query string from user
	 */
	private void search(String query) {
		LOGd(TAG, "Will do query " + query);
		Extractor e = Extractor.instance();
		List<Category> l = e.getList();
		Category c = e.getSearch();
		Search s = (Search) c.getSub(0);
		if (s == null) {
			LOGd(TAG, "No search sub-category");
			return;
		}
		s.parse(query);

		TopFragment t = getTop();
		if (t == null) return;

		int id = l.size() - 1;
		if (t.getPager().getCurrentItem() == id) {
			SubCategoryFragment sc = getSub(c.getName(), "");
			if (sc == null) return;
			LOGd(TAG, "Reloading page " + id);
			sc.load();
		} else {
			LOGd(TAG, "Flipping to page " + (id));
			t.getPager().setCurrentItem(id);
		}

	}

	/**
	 * Play (archived) program
	 * 
	 * @param p
	 *            Program
	 */
	public void playProgram(Program p) {
		LOGd(TAG,
                "Got program " + p.getTitle() + " w/links " + "l="
                        + p.getLink(Program.LOW) + " " + "m="
                        + p.getLink(Program.MEDIUM) + " " + "h="
                        + p.getLink(Program.HIGH)
        );
        String url = "";
        switch (Settings.getPreferredBitRate(this)) {
            case 0: url = p.getLink(Program.LOW); break;
            case 1: url = p.getLink(Program.MEDIUM); break;
            case 2: url = p.getLink(Program.HIGH); break;
            default: url = p.getLink(Program.HIGH); break;
        }
		play(url, p.getTitle(),
		     p.getSerie() != null ? p.getSerie().getImage() : "", false);
	}

	/**
	 * Play live stream
	 * 
	 * @param lc
	 *            Live stream
	 */
	public void playLive(LiveChannel lc) {
		LOGd(TAG, "Clicked element. Should play live stream " + lc.getName()
                + ": " + lc.mStream);
		play(lc.mStream, lc.getName() + ": " + lc.getNow().mTitle,
		     lc.getImage(), true);

	}

	/**
	 * Play it
	 * 
	 * @param url
	 *            Stream URL
	 * @param title
	 *            Title
	 * @param thumb
	 *            Thumb image
	 */
	private void play(String url, String title, String thumb, boolean live) {
		Toast.makeText(getApplicationContext(),
                getResources().getString(org.cholm.media.castdr.R.string.playing, title),
                Toast.LENGTH_SHORT).show();
		makeMedia(url, title, thumb, live);
		castPlay();
	}

	public void initMgr() {

	}

	/*
     * Handle volume up/down
     */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (!mCastMgr.isConnected()) {
			return super.onKeyDown(keyCode, event);
		} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
			changeVolume(0.05);
		} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
			changeVolume(-0.05);
		} else {
			return super.onKeyDown(keyCode, event);
		}
		return true;
	}

    /**
     * Change the volume on the cast device
     *
     * @param volumeIncrement How much to change it
     */
	private void changeVolume(double volumeIncrement) {
		if (mCastMgr == null) { return; }
		try {
			mCastMgr.incrementVolume(volumeIncrement);
		} catch (Exception e) {
            Utils.handleException(this, e);
		}
	}

    /**
     * Make media information to be send to cast device
     *
     * @param url    URL to stream
     * @param title  Title of stream
     * @param thumb  Thumb image
     * @param live   Live flag
     */
	protected void makeMedia(String url,
	                         String title,
	                         String thumb,
	                         boolean live) {
		MediaMetadata meta = new MediaMetadata();
		if (thumb != null && !thumb.isEmpty()) {
			final Uri uri = Uri.parse(thumb);
			// Add the image twice :-)
			meta.addImage(new WebImage(uri));
			meta.addImage(new WebImage(uri));
		}
		meta.putString(MediaMetadata.KEY_TITLE, title);

		MediaInfo.Builder b = new MediaInfo.Builder(url);
		b.setMetadata(meta);
		b.setContentType(live ? "application/x-mpegurl" : "video/mp4");
		b.setStreamType(live ? MediaInfo.STREAM_TYPE_LIVE
		                    : MediaInfo.STREAM_TYPE_BUFFERED);
        // if (live) b.setStreamDuration(1000);
		mSelectedMedia = b.build();
	}

    /**
     * Play on cast device
     */
	protected void castPlay() {
		try {
			int position = 0; // Dummy - offset in seconds
			boolean autoPlay = true;
			// mCastMgr.startCastControllerActivity(this, mSelectedMedia,
			// position, autoPlay);
			mCastMgr.loadMedia(mSelectedMedia, autoPlay, position);
			// mCastMgr.play(position);
		} catch (Exception e) {
            Utils.handleException(this, e);
		}
	}

	/**
	 * @author cholm
	 */
	private class DrawerItemClickListener implements
	                                     ListView.OnItemClickListener {
		@Override
		public void onItemClick(AdapterView<?> parent,
		                        View view,
		                        int position,
		                        long id) {
			selectItem(position, view);
		}
	}

	/**
	 * This loader loads the categories and then sends a message to the parent
	 * class to display the categories in a ViewPager.
	 * 
	 * @author cholm
	 */
	private class CategoryCallback implements
	                              LoaderManager.LoaderCallbacks<List<Category>> {
		private final String TAG = LogUtils.makeLogTag("Activity" +
                ".CategoryCallback");

		// --- Loader ---------------------------------------------------

		/**
		 * Create a loader
		 */
		@Override
		public Loader<List<Category>> onCreateLoader(int arg0, Bundle arg1) {
			LOGd(TAG, "Creating loader");
			return new CategoryLoader(Activity.this);
		}

		/**
		 * When the loader finished
		 */
		@Override
		public void onLoadFinished(Loader<List<Category>> ldr,
		                           List<Category> data) {
			LOGd(TAG, "Loader finished");

			// Set data on adapter
			mDrawerAdapter.setData(data);
			// Let it know that the content changed
			mDrawerAdapter.notifyDataSetChanged();
			// Remove the empty view of the list
			// mDrawerList.setEmptyView(mView.findViewById(R.id.emptyText));
			ldr.stopLoading();
			showCategories(data);

		}

		/**
		 * When loader is reset
		 */
		@Override
		public void onLoaderReset(Loader<List<Category>> arg0) {
			LOGd(TAG, "Reset loader");
		}
	}

	/**
	 * @author cholm
	 */
	private class DrawerToggle extends ActionBarDrawerToggle {

		public DrawerToggle(Activity a) {
			super(a, a.mDrawerLayout, org.cholm.media.castdr.R.drawable.ic_drawer,
			      org.cholm.media.castdr.R.string.drawer_open, org.cholm.media.castdr.R.string.drawer_close);
		}

		/**
		 * Called when a drawer has settled in a completely closed state.
		 */
		public void onDrawerClosed(View view) {
			super.onDrawerClosed(view);
			getSupportActionBar().setTitle(Activity.this.getTitle());
			// invalidateOptionsMenu(); // creates call to
			// onPrepareOptionsMenu()
		}

		/**
		 * Called when a drawer has settled in a completely open state.
		 */
		public void onDrawerOpened(View drawerView) {
			super.onDrawerOpened(drawerView);
			getSupportActionBar().setTitle(Activity.this.getTitle());
			// invalidateOptionsMenu(); // creates call to
			// onPrepareOptionsMenu()
		}
	}

	/**
	 * @author cholm
	 */
	private class SearchExpandListener implements OnActionExpandListener {
		@Override
		public boolean onMenuItemActionCollapse(MenuItem item) {
			// Do something when collapsed
			LOGd(TAG, "Search view collapsed");
			return true; // Return true to collapse action view
		}

		@Override
		public boolean onMenuItemActionExpand(MenuItem item) {
			// Do something when expanded
			LOGd(TAG, "Search view expanded");
			return true; // Return true to expand action view
		}
	}

	/**
	 * @author cholm
	 */
	private class SearchListener implements OnQueryTextListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see android.support.v7.widget.SearchView.OnQueryTextListener#
		 * onQueryTextChange(java.lang.String)
		 */
		@Override
		public boolean onQueryTextChange(String arg0) {
			// Do nothing on change
			return true;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see android.support.v7.widget.SearchView.OnQueryTextListener#
		 * onQueryTextSubmit(java.lang.String)
		 */
		@Override
		public boolean onQueryTextSubmit(String arg0) {
			search(arg0);
			return true;
		}

	}
}
/* EOF */
